/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package trab01_so;

import java.util.ArrayList;
import java.util.Date;
import javax.swing.JOptionPane;

/**
 *
 * @author Leonardo
 */
public class GM {

    String substituicao; //poderá ser LRU ou FIFO
    String escopo; //poderá ser LOCAL ou GLOBAL
    String processoAux;
    String tipoAcessoAux;
    String addrAux;
    MemoriaVirtual mv;
    MemoriaPrincipal mp;
    int idxFifo = 0;
    int tamMemPrinc;
    int tamMemSec;
    int tamQuadros;
    int tamImagem;
    int tamEndereco;
    // AppView view;
    ArrayList<ArrayList<Pagina>> tabelaPaginas = new ArrayList<ArrayList<Pagina>>();

    GM(MemoriaVirtual mvaux, MemoriaPrincipal mpaux) {
        this.mv = mvaux;
        this.mp = mpaux;
    }

    public boolean verificaLimiteImagem(String pid) {
        int contador = 0;
        for (int i = 0; i < mp.size(); i++) {
            if (mp.get(i).id.equalsIgnoreCase(pid)) {
                contador++;
            }

        }
        if (contador * this.tamQuadros >= this.tamImagem) {
            return false;
        } else {
            return true;
        }
    }

    public void recebeInstrucao(String instrucao) {

        if (this.checaInstrucao(instrucao)) {
            if (this.tipoAcessoAux.equalsIgnoreCase("W")) {
                System.out.println("entrou W");
                if (!mp.hasPage(addrAux) && (!mv.hasPage(addrAux))) {
                    System.out.println("não existe o endereço");
                    if (mp.size() * tamQuadros < this.tamMemPrinc) {

                        if (this.verificaLimiteImagem(this.processoAux)) {
                            Pagina p = new Pagina();
                            p.address = addrAux;
                            p.id = this.processoAux;
                            mp.add(p);
                            p.endereco = mp.get(mp.size() - 1).toString();
                            this.addTabelaPagina(p);
                        } else {
                            if (this.substituicao.equalsIgnoreCase("LRU")) {
                                int lruIdx = this.mp.getLRUIdx("LOCAL", this.processoAux);
                                if (lruIdx < 0) {
                                    JOptionPane.showMessageDialog(null, "Não é possível fazer a substituição em escopo local");
                                } else {
                                    Pagina p = new Pagina();
                                    p.address = addrAux;
                                    p.id = this.processoAux;
                                    Pagina aux = this.mp.get(lruIdx);
                                    this.mp.add(lruIdx, p);
                                    p.endereco = mp.get(mp.size() - 1).toString();
                                    this.addTabelaPagina(p);
                                    this.mv.add(aux);
                                }
                            } else if (this.substituicao.equalsIgnoreCase("FIFO")) {
                                Pagina p = new Pagina();
                                p.address = addrAux;
                                p.id = this.processoAux;
                                Pagina aux = this.mp.get(idxFifo);
                                this.mp.remove(idxFifo);
                                this.mp.add(p);
                                p.endereco = mp.get(mp.size() - 1).toString();
                                this.addTabelaPagina(p);
                                this.mv.add(aux);
                                idxFifo++;
                                if (idxFifo > (mp.size() - 1)) {
                                    idxFifo = 0;
                                }
                            }
                        }
                    } else if (mv.size() * tamQuadros < this.tamMemSec) {
                        if (this.escopo.equalsIgnoreCase("LOCAL")) {
                            if (this.substituicao.equalsIgnoreCase("LRU")) {
                                int idxLru = this.mp.getLRUIdx("LOCAL", this.processoAux);
                                if (idxLru < 0) {
                                    JOptionPane.showMessageDialog(null, "Não é possível fazer a substituição em escopo local");
                                } else {
                                    Pagina p = new Pagina();
                                    p.address = addrAux;
                                    p.id = this.processoAux;
                                    Pagina aux = this.mp.get(idxLru);
                                    this.mp.remove(idxLru);
                                    mv.add(aux);
                                    mp.add(p);
                                    this.addTabelaPagina(p);
                                    p.endereco = mp.get(mp.size() - 1).toString();
                                }
                            } else if (this.substituicao.equalsIgnoreCase("FIFO")) {
                                Pagina p = new Pagina();
                                p.address = addrAux;
                                p.id = this.processoAux;
                                Pagina aux = this.mp.get(idxFifo);
                                this.mp.remove(idxFifo);
                                mv.add(aux);
                                mp.add(p);
                                this.addTabelaPagina(p);
                                idxFifo++;
                                if (idxFifo > (mp.size() - 1)) {
                                    idxFifo = 0;
                                }
                            }
                        } else if (this.escopo.equalsIgnoreCase("GLOBAL")) {
                            if (this.substituicao.equalsIgnoreCase("LRU")) {
                                int idxLru = this.mp.getLRUIdx("GLOBAL", "");
                                if (idxLru < 0) {
                                    JOptionPane.showMessageDialog(null, "Não é possível fazer a substituição em escopo local");
                                } else {
                                    Pagina p = new Pagina();
                                    p.address = addrAux;
                                    p.id = this.processoAux;
                                    mv.add(this.mp.get(idxLru));
                                    mp.add(idxFifo, p);
                                    this.addTabelaPagina(p);
                                }
                            } else if (this.substituicao.equalsIgnoreCase("FIFO")) {
                                Pagina p = new Pagina();
                                p.address = addrAux;
                                p.id = this.processoAux;
                                Pagina aux = this.mp.get(idxFifo);
                                this.mp.remove(idxFifo);
                                mv.add(aux);
                                mp.add(p);
                                this.addTabelaPagina(p);
                                idxFifo++;
                                if (idxFifo > (mp.size() - 1)) {
                                    idxFifo = 0;
                                }
                            }
                        }
                    } else {
                        JOptionPane.showMessageDialog(null, "Memoria principal e Memoria virtual lotada.");
                    }
                } else { // a pagina do endereço addrAux existe em memória principal ou secundária
                    System.out.println("existe o endereço");
                    System.out.println("a pagina do endereço addrAux existe em memória principal ou secundária");
                    if (mp.hasPage(addrAux)) {
                        System.out.println("a pagina do endereço addrAux existe em memória principal");
                        mp.getPage(addrAux).lastAccess = new Date();
                    } else { //está em memória virtual
                        System.out.println("a pagina do endereço addrAux existe em memória virtual");
                        if (this.escopo.equalsIgnoreCase("LOCAL")) {
                            System.out.println("entrou escopo local");
                            int idMv = mv.getPageIdx(addrAux);
                            int idMp = mp.getLRUIdx("LOCAL", this.processoAux);
                            if (idMp < 0) {
                                JOptionPane.showMessageDialog(null, "Não é possível fazer a substituição em escopo local");
                            } else if (mp.get(idMp).id.equalsIgnoreCase(mv.get(idMv).id) && this.processoAux.equalsIgnoreCase(mv.get(idMv).id)) {
                                //se o id do processo que quer escrever é igual ao id do processo gravado no 
                                //espaço onde está sendo feita a escrita
                                if (this.substituicao.equalsIgnoreCase("LRU")) {
                                    System.out.println("entrou lru");
                                    //faz swap
                                    //idMp = mp.getLRUIdx("LOCAL", this.processoAux);
                                    System.out.println("pegou indice da mp: " + idMp);
                                    Pagina paginaDaMemVirtual = mv.get(mv.getPageIdx(addrAux));
                                    Pagina pgMpAux = mp.get(idMp);
                                    paginaDaMemVirtual.lastAccess = new Date();
                                    mv.set(idMv, pgMpAux);
                                    String endAux = pgMpAux.endereco;
                                    pgMpAux.endereco = paginaDaMemVirtual.endereco;
                                    paginaDaMemVirtual.endereco = endAux;
                                    mp.set(idMp, paginaDaMemVirtual);
                                    System.out.println("Executou LRU");
                                } else if (this.substituicao.equalsIgnoreCase("FIFO")) {
                                    int idxMvAux = mv.getPageIdx(addrAux);
                                    Pagina pgMvAux = mv.get(idxMvAux);
                                    Pagina pgMpAux = mp.get(idxFifo);
                                    mv.set(idxMvAux, pgMpAux);
                                    pgMvAux.lastAccess = new Date();
                                    mp.set(idxFifo, pgMvAux);
                                    String endAux = pgMpAux.endereco;
                                    pgMpAux.endereco = pgMvAux.endereco;
                                    pgMvAux.endereco = endAux;
                                    idxFifo++;
                                    if (idxFifo > (mp.size() - 1)) {
                                        idxFifo = 0;
                                    }
                                }
                            } else {
                                JOptionPane.showMessageDialog(null, "Não foi possível fazer a escrita pois não foi possível realizar a substituição devido ao escopo,\nou foi realizada uma tentativa de acesso à memória de outro processo.");
                            }
                        } else if (this.escopo.equalsIgnoreCase("GLOBAL")) {
                            int idMv = mv.getPageIdx(addrAux);
                            if (this.processoAux.equalsIgnoreCase(mv.get(idMv).id)) {
                                //se o id do processo que quer escrever é igual ao id do processo gravado no 
                                //espaço onde está sendo feita a escrita 
                                if (this.substituicao.equalsIgnoreCase("LRU")) { //faz swap 
                                    int idMp = mp.getLRUIdx("GLOBAL", "");
                                    Pagina paginaDaMemVirtual = mv.get(mv.getPageIdx(addrAux));
                                    Pagina pgMpAux = mp.get(idMp);
                                    String endAux = pgMpAux.endereco;
                                    pgMpAux.endereco = paginaDaMemVirtual.endereco;
                                    paginaDaMemVirtual.endereco = endAux;
                                    paginaDaMemVirtual.lastAccess = new Date();
                                    mv.set(idMv, pgMpAux);
                                    mp.set(idMp, paginaDaMemVirtual);
                                } else if (this.substituicao.equalsIgnoreCase("FIFO")) {
                                    int idxMvAux = mv.getPageIdx(addrAux);
                                    Pagina pgMvAux = mv.get(idxMvAux);
                                    Pagina pgMpAux = mp.get(idxFifo);
                                    String endAux = pgMpAux.endereco;
                                    pgMpAux.endereco = pgMvAux.endereco;
                                    pgMvAux.endereco = endAux;
                                    mv.set(idxMvAux, pgMpAux);
                                    pgMvAux.lastAccess = new Date();
                                    mp.set(idxFifo, pgMvAux);
                                    idxFifo++;
                                    if (idxFifo > (mp.size() - 1)) {
                                        idxFifo = 0;
                                    }
                                }
                            } else {
                                JOptionPane.showMessageDialog(null, "escopo local, não foi possível fazer a substituição");
                            }
                        }
                    }
                }
            } //Então é leitura(R)
            else {
                if (mp.hasPage(addrAux)) {
                    mp.getPage(addrAux).lastAccess = new Date();  //altera data de acesso
                } else if (mv.hasPage(addrAux)) {
                    if (this.escopo.equalsIgnoreCase("LOCAL")) {
                        if (this.substituicao.equalsIgnoreCase("LRU")) {
                            int idxMvAux = mv.getPageIdx(addrAux);
                            Pagina pgMvAux = mv.get(idxMvAux);
                            int idxAux = this.mp.getLRUIdx("LOCAL", pgMvAux.id);
                            Pagina pgMpAux = mp.get(idxAux);
                            String endAux = pgMpAux.endereco;
                            pgMpAux.endereco = pgMvAux.endereco;
                            pgMvAux.endereco = endAux;
                            if (idxAux < 0) {
                                JOptionPane.showMessageDialog(null, "Não é possível fazer a substituição em escopo local");
                            } else {
                                mv.set(idxMvAux, pgMpAux);
                                pgMvAux.lastAccess = new Date();
                                mp.set(idxAux, pgMvAux);
                            }
                        } else if (this.substituicao.equalsIgnoreCase("FIFO")) {
                            int idxMvAux = mv.getPageIdx(addrAux);
                            Pagina pgMvAux = mv.get(idxMvAux);
                            Pagina pgMpAux = mp.get(idxFifo);
                            String endAux = pgMvAux.endereco;
                            pgMvAux.endereco = pgMpAux.endereco;
                            pgMpAux.endereco = endAux;
                            mv.set(idxMvAux, pgMpAux);
                            pgMvAux.lastAccess = new Date();
                            mp.set(idxFifo, pgMvAux);
                            idxFifo++;
                            if (idxFifo > (mp.size() - 1)) {
                                idxFifo = 0;
                            }
                        }
                    } else if (this.escopo.equalsIgnoreCase("GLOBAL")) {
                        if (this.substituicao.equalsIgnoreCase("LRU")) {
                            int idxMvAux = mv.getPageIdx(addrAux);
                            Pagina pgMvAux = mv.get(idxMvAux);
                            int idxAux = this.mp.getLRUIdx("GLOBAL", "");
                            Pagina pgMpAux = mv.get(idxAux);
                            String endAux = pgMvAux.endereco;
                            pgMvAux.endereco = pgMpAux.endereco;
                            pgMpAux.endereco = endAux;
                            if (idxAux < 0) {
                                JOptionPane.showMessageDialog(null, "Não há página em memória principal que possa ser substituída");
                            } else {
                                mv.set(idxMvAux, mp.get(idxAux));
                                pgMvAux.lastAccess = new Date();
                                mp.set(idxAux, pgMvAux);
                            }
                        } else if (this.substituicao.equalsIgnoreCase("FIFO")) {
                            int idxMvAux = mv.getPageIdx(addrAux);
                            Pagina pgMvAux = mv.get(idxMvAux);
                            Pagina pgMpAux = mp.get(idxFifo);
                            String endAux = pgMpAux.endereco;
                            pgMpAux.endereco = pgMvAux.endereco;
                            pgMvAux.endereco = endAux;
                            mv.set(idxMvAux, pgMpAux);
                            pgMvAux.lastAccess = new Date();
                            mp.set(idxFifo, pgMvAux);
                            idxFifo++;
                            if (idxFifo > (mp.size() - 1)) {
                                idxFifo = 0;
                            }
                        }
                    }
                } else {
                    JOptionPane.showMessageDialog(null, "Erro: Falta de página");
                }
            }
        } else {
            JOptionPane.showMessageDialog(null, "Erro:Formatação da instrução incompativel. " + instrucao);
        }
    }

    public void addTabelaPagina(Pagina p) {
        boolean adicionou = false;
        for (int i = 0; i < tabelaPaginas.size(); i++) {
            ArrayList<Pagina> aux = tabelaPaginas.get(i);
            if (aux.get(0).id.equalsIgnoreCase(this.processoAux)) {
                aux.add(p);
                adicionou = true;
            }
        }
        if (!adicionou) {
            ArrayList<Pagina> aux = new ArrayList<Pagina>();
            aux.add(p);
            this.tabelaPaginas.add(aux);
        }
    }

    public boolean checaInstrucao(String instrucao) {

        String[] partes = instrucao.split(" ");
        //System.out.println("Partes[0]:"+partes[0]+"\nPartes[1]:"+partes[1]+"\nPartes[2]:"+partes[2]);
        if (!partes[1].equalsIgnoreCase("R") && !partes[1].equalsIgnoreCase("W")) {
            return false;
        } else {
            this.tipoAcessoAux = partes[1];
        }

        this.processoAux = partes[0];
        this.addrAux = partes[2];

        return true;
    }

    /*
     * public void escopoLocalW() { int idMv = mv.getPageIdx(addrAux); if
     * (this.processoAux.equalsIgnoreCase(mv.get(idMv).id)) { //se o id do
     * processo que quer escrever é igual ao id do processo gravado no //espaço
     * onde está sendo feita a escrita if
     * (this.substituicao.equalsIgnoreCase("LRU")) { //faz swap int idMp =
     * mp.getLRUIdx("LOCAL", this.processoAux); Pagina paginaDaMemVirtual =
     * mv.get(mv.getPageIdx(addrAux)); paginaDaMemVirtual.lastAccess = new
     * Date(); mv.add(idMv, mp.get(idMp)); mp.add(idMp, paginaDaMemVirtual); }
     * else if (this.substituicao.equalsIgnoreCase("FIFO")) { int idxMvAux =
     * mv.getPageIdx(addrAux); Pagina pgMvAux = mv.get(idxMvAux);
     * mv.set(idxMvAux, mp.get(idxFifo)); pgMvAux.lastAccess = new Date();
     * mp.set(idxFifo, pgMvAux); idxFifo++; if (idxFifo > (mp.size() - 1)) {
     * idxFifo = 0; } } } else { JOptionPane.showMessageDialog(null, "escopo
     * local, não foi possível fazer a substituição"); } }
     *
     * public void escopoLocalR() { if
     * (this.substituicao.equalsIgnoreCase("LRU")) { int idxMvAux =
     * mv.getPageIdx(addrAux); Pagina pgMvAux = mv.get(idxMvAux); int idxAux =
     * this.mp.getLRUIdx("LOCAL", pgMvAux.id); if (idxAux < 0) {
     * JOptionPane.showMessageDialog(null, "Processo não se encontra em memoria
     * principal"); } else { mv.set(idxMvAux, mp.get(idxAux));
     * pgMvAux.lastAccess = new Date(); mp.set(idxAux, pgMvAux); } } else if
     * (this.substituicao.equalsIgnoreCase("FIFO")) { int idxMvAux =
     * mv.getPageIdx(addrAux); Pagina pgMvAux = mv.get(idxMvAux);
     * mv.set(idxMvAux, mp.get(idxFifo)); pgMvAux.lastAccess = new Date();
     * mp.set(idxFifo, pgMvAux); idxFifo++; if (idxFifo > (mp.size() - 1)) {
     * idxFifo = 0; } } }
     */
}
